package cn.blnp.net.flowable.transfer.gateway.parallel;

import lombok.extern.slf4j.Slf4j;
import org.flowable.engine.HistoryService;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.TaskService;
import org.flowable.engine.repository.Deployment;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.task.api.Task;
import org.flowable.task.api.TaskQuery;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;

import javax.annotation.Resource;
import java.util.List;
import java.util.stream.Collectors;

/**
 * <h3>网关-并行网关</h3>
 *
 * @author <a href="mailto:blnp.yibin@qq.com">lyb</a>
 * @version 1.0
 * @since 2025/5/19 16:48
 */
@Slf4j
@SpringBootTest
public class ParallelGatewayTest {

    @Resource
    private RepositoryService repositoryService;
    @Resource
    private RuntimeService runtimeService;
    @Resource
    private TaskService taskService;
    @Resource
    private HistoryService historyService;
    
    /**
     *  <h2>并行网关案例</h2>
     *  <p>
     *      并行网关能够在一个流程中用于进行并发建模处理，将单条线路拆分为多条路径并行执行，或者将多条路径合并处理。在一个流程模型中引入并发最直接的网关是并行网关，它基于进入和外出顺序流，
     *  有分支和合并两种行为，允许将流程拆分为多个分支，或者将多个分支合并。<br/>
     *      分支即并行拆分，流程从并行网关流出后，并行网关会为所有外出顺序流分别创建一个并发分支。需要注意的是，并行网关会忽略外出顺序流上的条件（不会解析条件）。顺序流中即使定义了条件也会被忽略，
     *  它的每个后续分支路径都会被无条件执行。<br/>
     *      合并即并行合并，所有到达并行网关的分支路径都将汇聚于此并等待，只有当所有进入顺序流的分支都到达后，流程才会通过并行网关。如果其中有分支未到达或中断，那么该并行网关将一直处于等待状态。<br/>
     *  并行网关允许多进多出。如果同一个并行网关有多条进入和多条外出顺序流，那么它就同时具有分支和合并功能。在这种情况下，并行网关会先合并所有进入顺序流，再将其拆分为多个并行分支往外流出。
     *  </p>
     *  <h3>流程案例</h3>
     *  <p>
     *      该流程为物料领用流程。并行网关parallelGateway1拆分了3个分支，分别流转到“财务经理审批”节点、“直属上级审批”节点和“物料管理员审批”节点。“财务经理审批”节点和“直属上级审批”节点后续分支被并行
     *  网关parallelGateway2合并继续流转到“部门主管审批”节点。并行网关parallelGateway3等待“部门主管审批”节点和“物料管理员审批”节点任务办理完成后，流程结束。<br/>
     *      <b>流程定义文件：</b>{@link flow/parallelGateway.bpmn20.xml}<br/>
     *      <b>流程预览图：</b><br/>
     *      <img src="https://cdn4.winhlb.com/2025/05/19/682af38b602c6.png" />
     *  </p>
     **/
    @Test
    public void test() {
        Deployment deploy = repositoryService.createDeployment()
                .name("并行网关演示案例")
                .addClasspathResource("flow/parallelGateway.bpmn20.xml")
                .deploy();
        log.info("部署流程定义：{}", deploy.getId());
        ProcessDefinition procDef = repositoryService.createProcessDefinitionQuery()
                .deploymentId(deploy.getId())
                .singleResult();
        log.info("流程定义编号：{}", procDef.getId());
        //启动流程实例
        ProcessInstance procInst = runtimeService.startProcessInstanceById(procDef.getId());
        TaskQuery taskQuery = taskService.createTaskQuery()
                .processInstanceId(procInst.getId());

        //查询任务
        List<Task> taskList1 = taskQuery.list();
        log.info("流程发起后，当前任务个数为：{}，分别为：{}", taskList1.size(),
                taskList1.stream().map(Task::getName)
                        .collect(Collectors.joining("，")));
        //办理前taskList1.size()个用户任务
        taskList1.stream().forEach(task -> {
            taskService.complete(task.getId());
            log.info("办理完成用户任务：{}", task.getName());
        });
        List<Task> taskList2 = taskQuery.list();
        //查询任务
        log.info("办理完前{}个任务后，当前流程的任务个数为{}，分别为：{}", taskList1.size(),
                taskList2.size(), taskList2.stream().map(Task::getName)
                        .collect(Collectors.joining(",")));
        //办理taskList2.size()个用户任务
        taskList2.stream().forEach(task -> {
            taskService.complete(task.getId());
            log.info("办理完成用户任务：{}", task.getName());
        });
        //查询剩余任务
        List<Task> taskList3 = taskQuery.list();
        log.info("最后剩余的用户任务数为：{}", taskList3.size());

        /**
         *  演示结果：
         *  2025-05-19 17:04:42.157  INFO 37676 --- [           main] c.b.n.f.t.g.p.ParallelGatewayTest        : 部署流程定义：4ca6ccf3-3490-11f0-8ff2-50ebf6ec8f6d
         * 2025-05-19 17:04:42.161  INFO 37676 --- [           main] c.b.n.f.t.g.p.ParallelGatewayTest        : 流程定义编号：parallelGatewayProcess:1:4d0da516-3490-11f0-8ff2-50ebf6ec8f6d
         * 2025-05-19 17:04:42.256  INFO 37676 --- [           main] c.b.n.f.t.g.p.ParallelGatewayTest        : 流程发起后，当前任务个数为：3，分别为：直属上级审批，财务经理审批，物料管理员审批
         * 2025-05-19 17:04:42.280  INFO 37676 --- [           main] c.b.n.f.t.g.p.ParallelGatewayTest        : 办理完成用户任务：直属上级审批
         * 2025-05-19 17:04:42.306  INFO 37676 --- [           main] c.b.n.f.t.g.p.ParallelGatewayTest        : 办理完成用户任务：财务经理审批
         * 2025-05-19 17:04:42.325  INFO 37676 --- [           main] c.b.n.f.t.g.p.ParallelGatewayTest        : 办理完成用户任务：物料管理员审批
         * 2025-05-19 17:04:42.327  INFO 37676 --- [           main] c.b.n.f.t.g.p.ParallelGatewayTest        : 办理完前3个任务后，当前流程的任务个数为1，分别为：部门主管审批
         * 2025-05-19 17:04:42.379  INFO 37676 --- [           main] c.b.n.f.t.g.p.ParallelGatewayTest        : 办理完成用户任务：部门主管审批
         * 2025-05-19 17:04:42.381  INFO 37676 --- [           main] c.b.n.f.t.g.p.ParallelGatewayTest        : 最后剩余的用户任务数为：0
         **/
    }
}
